﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Leo.Awesome.AppPlatform.AbpExceptionHandler;
using Leo.Awesome.AppPlatform.MongoDB;
using Leo.Core.AutoReponseWarpper;
using Leo.Core.HttpApi.Host.Host;
using Leo.Core.HttpApi.Host.SwaggerFilter;
using Leo.Core.Jwt;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerGen;
using Volo.Abp;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.Autofac;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.MongoDB;
using Volo.Abp.Swashbuckle;
using Volo.Abp.Timing;
using Volo.Abp.VirtualFileSystem;

namespace Leo.Awesome.AppPlatform;

[DependsOn(
    typeof(AppPlatformHttpApiModule),
    typeof(AbpAutofacModule),
    typeof(AppPlatformApplicationModule),
    typeof(AppPlatformMongoDbModule),
    typeof(AbpAspNetCoreSerilogModule),
    typeof(AbpSwashbuckleModule),
    typeof(AutoWrapperModule))]
public class AppPlatformHttpApiHostModule : AbpModule
{

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();
        var hostingEnvironment = context.Services.GetHostingEnvironment();
        context.Services.AddTransient<IExceptionToErrorInfoConverter, ApplicationExceptionToErrorInfoConverter>();
        context.Services.AddTransient<IHttpExceptionStatusCodeFinder, ApplicationHttpExceptionStatusCodeFinder>();
        
        // 配置jwt
        context.ConfigureJwtTokenService();
        context.ConfigureJwtAuthentication();

        // 配置CSRF
        context.ConfigureAbpAntiForgeryOptions(new List<string> { "POST", "PUT", "DELETE" });

        // 自动Api
        context.ConfigureConventionalControllers<AppPlatformHttpApiClientModule>();
        // 虚拟文件配置
        ConfigureVirtualFileSystem(context);

        // 本地化配置
        ConfigureLocalization();

        // 配置时间格式
        ConfigClockDateKind(context);

        // 配置swagger
        ConfigureSwaggerServices(context);

        // 配置跨域
        ConfigureCors(context, configuration);
    
    }


    public void ConfigureSwaggerServices(ServiceConfigurationContext context)
    {
        context.Services.AddAbpSwaggerGen(delegate (SwaggerGenOptions options)
        {
            options.SwaggerDoc("v1", new OpenApiInfo
            {
                Title = "Leo",
                Version = "v1"
            });

            options.DocInclusionPredicate((string docName, ApiDescription description) => true);
            options.CustomSchemaIds((Type type) => type.FullName);
            options.IgnoreObsoleteActions();
            options.IgnoreObsoleteProperties();
            options.EnableAnnotations();
            options.DocumentFilter<HiddenSwaggerApiFilter>(Array.Empty<object>());

            options.OperationFilter<AddResponseHeadersFilter>(Array.Empty<object>());
            options.OperationFilter<AppendAuthorizeToSummaryOperationFilter>(Array.Empty<object>());
            options.OperationFilter<SecurityRequirementsOperationFilter>(Array.Empty<object>());

            options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
            {
                Description = "JWT授权(数据将在请求头中进行传递)直接在下面框中输入Bearer {token}(注意两者之间是一个空格)",
                Name = "Authorization",
                Scheme = "Bearer",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.ApiKey
            });


            options.AddSecurityRequirement(new OpenApiSecurityRequirement {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "oauth2"
                        }
                    },
                    new string[0]
                }
            });
        });

        context.Services.AddSwaggerGen(c =>
        {
            foreach (var item in XmlCommentsFilePath)
            {
                c.IncludeXmlComments(item);
            }
        });
        context.Services.AddSwaggerGenNewtonsoftSupport();
    }
    static List<string> XmlCommentsFilePath
    {
        get
        {
            var basePath = PlatformServices.Default.Application.ApplicationBasePath;
            DirectoryInfo d = new DirectoryInfo(basePath);
            FileInfo[] files = d.GetFiles("*.xml");
            var xmls = files.Select(a => Path.Combine(basePath, a.FullName)).ToList();
            return xmls;
        }
    }

    private void ConfigClockDateKind(ServiceConfigurationContext context)
    {
        context.Services.Configure<AbpClockOptions>(options =>
        {
            options.Kind = DateTimeKind.Local;//系统采用本地(北京)时间
        });

        context.Services.Configure<AbpMongoDbOptions>(options =>
        {
            options.UseAbpClockHandleDateTime = false;//停用Abp的MongoDB时间序列化对象,因为嵌套类型中包含的DateTime属性不会被处理
        });
    }

    private void ConfigureVirtualFileSystem(ServiceConfigurationContext context)
    {
        var hostingEnvironment = context.Services.GetHostingEnvironment();

        if (hostingEnvironment.IsDevelopment())
        {
            Configure<AbpVirtualFileSystemOptions>(options =>
            {
                options.FileSets.ReplaceEmbeddedByPhysical<AppPlatformDomainSharedModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}Leo.Awesome.AppPlatform.Domain.Shared"));
                options.FileSets.ReplaceEmbeddedByPhysical<AppPlatformDomainModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}Leo.Awesome.AppPlatform.Domain"));
                options.FileSets.ReplaceEmbeddedByPhysical<AppPlatformApplicationContractsModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}Leo.Awesome.AppPlatform.Application.Contracts"));
                options.FileSets.ReplaceEmbeddedByPhysical<AppPlatformApplicationModule>(
                    Path.Combine(hostingEnvironment.ContentRootPath,
                        $"..{Path.DirectorySeparatorChar}Leo.Awesome.AppPlatform.Application"));
            });
        }
    }

    // 本地化配置
    private void ConfigureLocalization()
    {
        Configure(delegate (AbpLocalizationOptions options)
        {
            options.Languages.Add(new LanguageInfo("en", "en", "English"));
            options.Languages.Add(new LanguageInfo("zh-Hans", "zh-Hans", "简体中文"));
            options.Languages.Add(new LanguageInfo("zh-Hant", "zh-Hant", "繁體中文"));
        });
        Configure(delegate (RequestLocalizationOptions options)
        {
            List<CultureInfo> list = new List<CultureInfo>
                {
                    new CultureInfo("en"),
                    new CultureInfo("zh-Hans"),
                    new CultureInfo("zh-Hant")
                };
            options.DefaultRequestCulture = new RequestCulture("zh-Hans");
            options.SupportedCultures = list;
            options.SupportedUICultures = list;
        });
    }

    private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddCors(options =>
        {
            options.AddDefaultPolicy(builder =>
               {
                builder
                    .WithOrigins(
                        configuration["App:CorsOrigins"]
                            .Split(",", StringSplitOptions.RemoveEmptyEntries)
                            .Select(o => o.RemovePostFix("/"))
                            .ToArray()
                    )
                    .WithAbpExposedHeaders()
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        var env = context.GetEnvironment();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseAbpRequestLocalization();
        app.UseCorrelationId();
        app.UseStaticFiles();
        app.UseRouting();

        //自动返回数据格式化
        app.UseApiResponseAndExceptionWrapper(new AutoWrapperOptions
        {
            ShowIsErrorFlagForSuccessfulResponse = true,
            ExcludePaths = new AutoWrapperExcludePath[] {
                    // 严格匹配
                    new AutoWrapperExcludePath("/v1/api-docs", ExcludeMode.StartWith),
                }
        });
        app.UseAuthentication();
        app.UseAbpClaimsMap();
        app.UseAuthorization();
        app.UseCors();

        app.UseAuditing();
        app.UseAbpSerilogEnrichers();
        app.UseUnitOfWork();

        app.UseConfiguredEndpoints();

        app.UseSwagger();
        app.UseSwaggerUI(options =>
        {
            options.InjectStylesheet("/swagger-ui/custom.css");
            options.SwaggerEndpoint("/swagger/v1/swagger.json", "Leo");
        });
    }
}
